package main

import (
	"fmt"
	"net"
	"net/http"
	"os"
	"time"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/dialog"
	"fyne.io/fyne/v2/driver/desktop"
	"fyne.io/fyne/v2/theme"
	"fyne.io/fyne/v2/widget"
)

var defaultHttpClient = &http.Client{
	Transport: &http.Transport{
		MaxIdleConns:          1000,
		MaxIdleConnsPerHost:   100,
		MaxConnsPerHost:       100,
		IdleConnTimeout:       90 * time.Second,
		TLSHandshakeTimeout:   10 * time.Second,
		ExpectContinueTimeout: 5 * time.Second,
		ResponseHeaderTimeout: 10 * time.Second,
		DisableKeepAlives:     false,
		DisableCompression:    false,
	},
	Timeout: 30 * time.Second,
}

// ExecutionRecord 与服务端保持一致的结构体
type ExecutionRecord struct {
	ScriptName  string    `json:"script_name"`
	ExecutedAt  time.Time `json:"executed_at"`
	Output      string    `json:"output"`
	Error       string    `json:"error,omitempty"`
	ExitCode    int       `json:"exit_code"`
	ClientIP    string    `json:"client_ip,omitempty"`
	ExecuteType string    `json:"execute_type"`
}

type ScriptClient struct {
	serverURL string
	window    fyne.Window
}

// ADD NEW CODE: HoverButton type to show hand cursor on hover
// HoverButton is a button that changes the mouse cursor to a hand pointer when hovered.
type HoverButton struct {
	widget.Button
}

// NewHoverButton creates a new HoverButton with the given parameters.
func NewHoverButton(label string, icon fyne.Resource, tapped func()) *HoverButton {
	hb := &HoverButton{}
	hb.Text = label
	hb.Icon = icon
	hb.OnTapped = tapped
	hb.ExtendBaseWidget(hb)
	return hb
}

// Ensure HoverButton satisfies the desktop.Cursorable interface so Fyne will
// automatically display the hand pointer when the mouse is over the button.
var _ desktop.Cursorable = (*HoverButton)(nil)

// Cursor returns the cursor that should be used when the pointer hovers over
// the HoverButton. By returning desktop.PointerCursor we get the familiar hand
// cursor associated with clickable widgets.
func (b *HoverButton) Cursor() desktop.Cursor {
	return desktop.PointerCursor
}

func NewScriptClient() *ScriptClient {
	// 从环境变量获取服务器地址，如果没有则使用默认值
	serverURL := os.Getenv("SCRIPT_SERVER_URL")
	if serverURL == "" {
		serverURL = "http://yuansu02.vip.cpolar.cn:8099" // 默认值
	}

	return &ScriptClient{
		serverURL: serverURL,
	}
}

// getLocalIPv4 获取本机IPv4地址
func getLocalIPv4() string {
	// 方法1：通过连接外部地址获取本机出站IP
	conn, err := net.Dial("udp", "8.8.8.8:80")
	if err == nil {
		defer conn.Close()
		localAddr := conn.LocalAddr().(*net.UDPAddr)
		if ip := localAddr.IP.To4(); ip != nil {
			return ip.String()
		}
	}

	// 方法2：遍历网络接口获取IPv4地址
	interfaces, err := net.Interfaces()
	if err != nil {
		return "127.0.0.1"
	}

	for _, iface := range interfaces {
		// 跳过回环接口和未启用的接口
		if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
			continue
		}

		addrs, err := iface.Addrs()
		if err != nil {
			continue
		}

		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}

			// 只返回IPv4地址，跳过回环地址
			if ip != nil && ip.To4() != nil && !ip.IsLoopback() {
				// 优先返回私有网络地址
				if isPrivateIPv4(ip) {
					return ip.String()
				}
			}
		}
	}

	return "127.0.0.1"
}

// isPrivateIPv4 判断是否为私有IPv4地址
func isPrivateIPv4(ip net.IP) bool {
	if ip == nil {
		return false
	}

	// 私有IPv4地址范围
	privateRanges := []string{
		"10.0.0.0/8",
		"172.16.0.0/12",
		"192.168.0.0/16",
	}

	for _, cidr := range privateRanges {
		_, subnet, _ := net.ParseCIDR(cidr)
		if subnet != nil && subnet.Contains(ip) {
			return true
		}
	}
	return false
}

func (sc *ScriptClient) executeScript() {
	// 获取本机IPv4地址
	localIP := getLocalIPv4()

	// 创建HTTP请求
	url := sc.serverURL + "/run_default"
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		fyne.Do(func() {
			dialog.ShowError(fmt.Errorf("创建请求失败: %v", err), sc.window)
		})
		return
	}

	// 设置X-Real-IP请求头
	req.Header.Set("X-Real-IP", localIP)

	// 发送请求
	resp, err := defaultHttpClient.Do(req)
	if err != nil {
		fyne.Do(func() {
			dialog.ShowError(fmt.Errorf("连接服务器失败: %v", err), sc.window)
		})
		return
	}
	defer resp.Body.Close()

	// 检查HTTP状态码
	if resp.StatusCode != http.StatusOK {
		fyne.Do(func() {
			dialog.ShowError(fmt.Errorf("执行失败，请联系管理员"), sc.window)
		})
		return
	}

	fyne.Do(func() {
		dialog.ShowInformation("执行成功", "执行成功", sc.window)
	})
}

// performScriptRequest 执行脚本请求，返回错误信息，由调用方负责展示结果。
func (sc *ScriptClient) performScriptRequest() error {
	// 获取本机IPv4地址
	localIP := getLocalIPv4()

	// 创建HTTP请求
	url := sc.serverURL + "/run_default"
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return fmt.Errorf("创建请求失败: %v", err)
	}

	// 设置X-Real-IP请求头
	req.Header.Set("X-Real-IP", localIP)

	// 发送请求
	resp, err := defaultHttpClient.Do(req)
	if err != nil {
		return fmt.Errorf("连接服务器失败: %v", err)
	}
	defer resp.Body.Close()

	// 检查HTTP状态码
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("执行失败，请联系管理员")
	}

	return nil
}

func (sc *ScriptClient) showExecutionResult(record *ExecutionRecord) {
	// 格式化执行时间
	executedTime := record.ExecutedAt.Format("2006-01-02 15:04:05")

	// 根据退出代码显示简单的成功/失败信息
	var title, message string
	if record.ExitCode == 0 {
		title = "执行成功"
		message = fmt.Sprintf("✅ 脚本执行成功\n\n脚本名称: %s\n执行时间: %s",
			record.ScriptName, executedTime)
	} else {
		title = "执行失败"
		message = fmt.Sprintf("❌ 脚本执行失败\n\n脚本名称: %s\n执行时间: %s\n退出代码: %d",
			record.ScriptName, executedTime, record.ExitCode)
	}

	// 使用简单的信息对话框显示结果，不泄露敏感信息
	dialog.ShowInformation(title, message, sc.window)
}

func (sc *ScriptClient) createGUI() {
	myApp := app.New()
	myWindow := myApp.NewWindow("执行脚本")
	myWindow.Resize(fyne.NewSize(500, 400))
	sc.window = myWindow

	// 主执行按钮
	var executeBtn *HoverButton
	executeBtn = NewHoverButton("执行脚本", theme.MediaPlayIcon(), func() {
		// 禁用按钮，避免重复点击
		executeBtn.Disable()
		executeBtn.SetText("执行中...")
		executeBtn.Refresh()

		// 显示无限进度对话框
		progressDlg := dialog.NewProgressInfinite("执行中", "正在执行脚本，请稍候...", sc.window)
		progressDlg.Show()

		// 在后台执行脚本请求，完成后关闭对话框并恢复按钮
		go func() {
			err := sc.performScriptRequest()

			// 返回主线程更新UI
			fyne.Do(func() {
				progressDlg.Hide()
				executeBtn.Enable()
				executeBtn.SetText("执行脚本")
				executeBtn.Refresh()

				if err != nil {
					dialog.ShowError(err, sc.window)
				} else {
					dialog.ShowInformation("执行成功", "执行成功", sc.window)
				}
			})
		}()
	})
	executeBtn.Importance = widget.HighImportance
	// 设置按钮固定大小，不占满整个窗口
	executeBtn.Resize(fyne.NewSize(200, 50))

	// 关于按钮
	aboutBtn := NewHoverButton("关于", theme.InfoIcon(), func() {
		aboutText := fmt.Sprintf(`脚本执行客户端

版本: 1.0.0
服务器地址: %s

用于远程执行服务器端的默认脚本。

环境变量配置:
SCRIPT_SERVER_URL - 服务器地址`, sc.serverURL)

		dialog.ShowInformation("关于", aboutText, myWindow)
	})
	aboutBtn.Importance = widget.LowImportance

	// 将执行按钮居中显示，添加顶部间距
	executeBtnContainer := container.NewVBox(
		widget.NewLabel(""), // 顶部间距
		widget.NewLabel(""), // 更多间距
		container.NewCenter(executeBtn),
	)

	// 底部按钮容器，居中显示
	bottomBtnContainer := container.NewHBox(aboutBtn)
	bottomBtnContainerCentered := container.NewCenter(bottomBtnContainer)

	// 使用边框布局，移除顶部服务器地址显示
	content := container.NewBorder(
		nil, // 顶部区域：空
		// 底部区域：设置和关于按钮
		container.NewVBox(
			widget.NewLabel(""), // 空白行
			widget.NewSeparator(),
			widget.NewLabel(""), // 空白行
			bottomBtnContainerCentered,
			widget.NewLabel(""), // 底部空白
		),
		nil, // 左侧
		nil, // 右侧
		// 中心区域：执行按钮
		executeBtnContainer,
	)

	myWindow.SetContent(content)
	myWindow.ShowAndRun()
}

func (sc *ScriptClient) showSettings() {
	settingsWindow := fyne.CurrentApp().NewWindow("设置")
	settingsWindow.Resize(fyne.NewSize(500, 200))

	// 服务器地址输入
	urlEntry := widget.NewEntry()
	urlEntry.SetText(sc.serverURL)
	urlEntry.SetPlaceHolder("http://localhost:8080")

	// 测试连接按钮
	testBtn := widget.NewButton("测试连接", func() {
		testURL := urlEntry.Text
		if testURL == "" {
			testURL = "http://localhost:8080"
		}

		// 测试连接
		client := &http.Client{Timeout: 5 * time.Second}
		resp, err := client.Get(testURL + "/run_default")
		if err != nil {
			dialog.ShowError(fmt.Errorf("连接失败: %v", err), settingsWindow)
			return
		}
		resp.Body.Close()

		if resp.StatusCode == http.StatusNotFound {
			dialog.ShowInformation("连接成功", "服务器连接正常（无默认脚本）", settingsWindow)
		} else {
			dialog.ShowInformation("连接成功", "服务器连接正常", settingsWindow)
		}
	})

	// 保存按钮
	saveBtn := widget.NewButton("保存", func() {
		newURL := urlEntry.Text
		if newURL == "" {
			newURL = "http://localhost:8080"
		}
		sc.serverURL = newURL
		settingsWindow.Close()

		// 刷新主窗口显示
		sc.createGUI()
	})
	saveBtn.Importance = widget.HighImportance

	// 取消按钮
	cancelBtn := widget.NewButton("取消", func() {
		settingsWindow.Close()
	})

	// 布局
	form := container.NewVBox(
		widget.NewLabel("服务器地址:"),
		urlEntry,
		widget.NewSeparator(),
		container.NewHBox(testBtn, saveBtn, cancelBtn),
	)

	settingsWindow.SetContent(form)
	settingsWindow.Show()
}

func main() {
	client := NewScriptClient()
	client.createGUI()
}
